using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

using EntModApp.EntServices;
using EntModApp.EntServices.Data;

namespace DemoApp.DataAccess 
{
  public partial class FavoriteQueryableRepository : 
    DbContextQueryableRepository<DemoAppModelDbContext, Favorite>,
    IQueryableFavoriteRepository
  {
    public FavoriteQueryableRepository(DemoAppModelDbContext context)
      : this(context, false)
    {
    }

    public FavoriteQueryableRepository()
      : this(new DemoAppModelDbContext(), true)
    {
    }

    protected FavoriteQueryableRepository(DemoAppModelDbContext context, bool ownContext)
      : base(context, context.Favorites, ownContext)
    {
    }
  }

  public partial class FavoriteDataRepository : 
    DbContextDataRepository<DemoAppModelDbContext, Favorite, FavoriteLoadSettings>,
    IFavoriteRepository
  {
    public FavoriteDataRepository(DemoAppModelDbContext context)
      : this(context, false)
    {
    }

    public FavoriteDataRepository()
      : this(new DemoAppModelDbContext(), true)
    {
    }

    protected FavoriteDataRepository(DemoAppModelDbContext context, bool ownContext)
      : base(context, context.Favorites, ownContext)
    {
    }

    public override IEnumerable<Favorite> RetrieveAll(out int total, bool retrieveTotal)
    {
      var query = new FavoriteLoadSettings
      {
        Paging = new PagingSettings { ComputeTotal = retrieveTotal }
      };
      return Retrieve(query, out total);
    }

    public override IEnumerable<Favorite> Retrieve(FavoriteLoadSettings query, out int total)
    {
      var dbQuery = DataSet.ApplyFavoriteLoadSettings(query, out total);
      return dbQuery.ToList();
    }

    public override int Count()
    {
      return DataSet.Count();
    }

    public override int Count(FavoriteLoadSettings query)
    {
      var dbQuery = DataSet.ApplyFavoriteFilterExpression(query.Filter);
      return dbQuery.Count();
    }
  }

  public static class FavoriteQueryableRepositoryExtensions
  {
    public static IQueryable<Favorite> ApplyFavoriteLoadSettings(this IQueryable<Favorite> query, FavoriteLoadSettings loadSettings, out int total)
    {
      if (loadSettings == null)
        loadSettings = new FavoriteLoadSettings();

      query = query.ApplyFavoriteInclusionSettings(loadSettings.Inclusion);

      query = query.ApplyFavoriteFilterExpression(loadSettings.Filter);

      query = query.ApplyFavoriteSortingSettings(loadSettings.Sorting);

      query = query.ApplyPagingSettings(loadSettings.Paging, out total);

      query = query.ApplyFavoriteSelectionSettings(loadSettings.Selection);

      return query;
    }

    public static IEnumerable<string> GetDirectFavoriteInclusionPaths(this FavoriteInclusionSettings inclusionSettings)
    {
      if (inclusionSettings.IncludeUser) yield return "User";
      if (inclusionSettings.IncludeSubject) yield return "Subject";
      if (inclusionSettings.IncludeSuperFavorites) yield return "SuperFavorites";

      yield break;
    }

    public static IEnumerable<string> GetFavoriteInclusionPaths(this FavoriteInclusionSettings inclusionSettings)
    {
      foreach (var path in inclusionSettings.GetDirectFavoriteInclusionPaths())
        yield return path;

      if (inclusionSettings.IncludeUser)
        foreach (var referencePath in inclusionSettings.IncludedUserSettings.Inclusion.GetUserInclusionPaths())
          yield return "User." + referencePath;
      if (inclusionSettings.IncludeSubject)
        foreach (var referencePath in inclusionSettings.IncludedSubjectSettings.Inclusion.GetSubjectInclusionPaths())
          yield return "Subject." + referencePath;
      if (inclusionSettings.IncludeSuperFavorites)
        foreach (var referencePath in inclusionSettings.IncludedSuperFavoritesSettings.Inclusion.GetFavoriteDerivedInclusionPaths())
          yield return "SuperFavorites." + referencePath;

      yield break;
    }

    public static IQueryable<Favorite> ApplyFavoriteInclusionSettings(this IQueryable<Favorite> query, FavoriteInclusionSettings inclusionSettings)
    {
      foreach (var path in inclusionSettings.GetFavoriteInclusionPaths())
        query = query.Include(path);

      return query;
    }

    public static IQueryable<Favorite> ApplyFavoriteFilterExpression(this IQueryable<Favorite> query, FavoriteFilterExpression filterExpression)
    {
      if (filterExpression.ByKey != null)
        query = query.Where(o => o.UserId == filterExpression.ByKey.UserId && o.SubjectId == filterExpression.ByKey.SubjectId);

      var parameter = Expression.Parameter(typeof(Favorite), "e");
      if (filterExpression.VoteFilter != null)
        query = query.Where(filterExpression.VoteFilter.CreateExpressionBody<Favorite, double?>(FavoritePropertyNames.Vote, parameter).ToLambda<Favorite>(parameter));

      if (filterExpression.Skip.HasValue)
        query = query.Skip(filterExpression.Skip.Value);
      if (filterExpression.Take.HasValue)
        query = query.Take(filterExpression.Take.Value);

      return query;
    }

    public static IQueryable<Favorite> ApplyFavoriteSortingSettings(this IQueryable<Favorite> query, FavoriteSortingSettings sortingSettings)
    {
      return query;
    }

    public static IQueryable<Favorite> ApplyFavoriteSelectionSettings(this IQueryable<Favorite> query, FavoriteSelectionSettings selectionSettings)
    {
      return query;
    }

  }
}
